package com.yeskery.nut.scan.advice;

import com.yeskery.nut.core.RequestApplicationContext;
import com.yeskery.nut.core.Controller;
import com.yeskery.nut.core.NutException;
import com.yeskery.nut.plugin.ApplicationContextSupportBasePlugin;
import com.yeskery.nut.scan.controller.AnnotationControllerInvocationHandler;
import com.yeskery.nut.scan.controller.AnnotationRequestMethodAttributes;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * 基础ControllerAdvice注解处理类
 * @author sprout
 * 2022-06-16 18:19
 */
public class BaseControllerAdviceAnnotationHandler extends ApplicationContextSupportBasePlugin {

    /** 日志对象 */
    private static final Logger logger = Logger.getLogger(BaseControllerAdviceAnnotationHandler.class.getName());

    /** 默认空包名 */
    protected static final String DEFAULT_EMPTY_PACKAGES = "__$--";

    /** 目标对象 */
    protected final Map<Class<?>, Object> targetMap = new HashMap<>();

    /** 包默认理器 */
    protected final Map<String, ControllerAdviceCombinationMetadataAttributes> packageDefaultMetadataMap = new HashMap<>();

    /**
     * 注册资源
     * @param basePackages 基础包
     * @param metadataAttributes 元数据对象
     */
    public void registerResource(String[] basePackages, ControllerAdviceCombinationMetadataAttributes metadataAttributes) {
        if (basePackages == null || basePackages.length == 0) {
            ControllerAdviceCombinationMetadataAttributes defaultMetadata = packageDefaultMetadataMap.get(DEFAULT_EMPTY_PACKAGES);
            if (defaultMetadata != null) {
                throw new NutException("Default Package Resource Already Bind.");
            }
            packageDefaultMetadataMap.put(DEFAULT_EMPTY_PACKAGES, metadataAttributes);
        } else {
            for (String basePackage : basePackages) {
                ControllerAdviceCombinationMetadataAttributes metadata = packageDefaultMetadataMap.get(basePackage);
                if (metadata != null) {
                    throw new NutException("Default Package [" + basePackage + "] Resource Already Bind.");
                }
                packageDefaultMetadataMap.put(basePackage, metadataAttributes);
            }
        }
    }

    /**
     * 获取Controller包名
     * @param controller controller对象
     * @return Controller包名
     */
    public String getControllerPackageName(Controller controller) {
        String controllerPackageName = null;
        // 尝试直接从Controller类名中获取
        if (controller.getClass().getPackage() != null) {
            controllerPackageName = controller.getClass().getPackage().getName();
        }
        // 尝试从应用上下文获取
        if (controllerPackageName == null) {
            Class<?> clazz = (Class<?>) RequestApplicationContext.getResource(AnnotationControllerInvocationHandler.ORIGINAL_CONTROLLER_CLASS_NAME);
            if (clazz != null && clazz.getPackage() != null) {
                controllerPackageName = clazz.getPackage().getName();
            }
        }
        // 尝试从注解Controller代理对象中获取
        if (controllerPackageName == null) {
            if (controller instanceof Proxy) {
                Field field = null;
                for (Field declaredField : controller.getClass().getSuperclass().getDeclaredFields()) {
                    if (InvocationHandler.class.isAssignableFrom(declaredField.getType())) {
                        field = declaredField;
                        break;
                    }
                }
                if (field != null) {
                    field.setAccessible(true);
                    try {
                        Object invocationHandler = field.get(controller);
                        if (invocationHandler instanceof AnnotationControllerInvocationHandler) {
                            AnnotationControllerInvocationHandler handler = (AnnotationControllerInvocationHandler) invocationHandler;
                            AnnotationRequestMethodAttributes annotationRequestMethodAttributes = handler.getAnnotationRequestMethodAttributes();
                            Class<?> type;
                            if (annotationRequestMethodAttributes != null
                                    && (type = annotationRequestMethodAttributes.getType()) != null
                                    && type.getPackage() != null) {
                                controllerPackageName = type.getPackage().getName();
                            }
                        }
                    } catch (IllegalAccessException e) {
                        logger.logp(Level.WARNING, BaseControllerAdviceAnnotationHandler.class.getName(),
                                "getControllerPackageName", "Obtain Controller Class Package Name Failure.", e);
                    }
                    field.setAccessible(false);
                }
            }
        }
        // 无法获取到Controller包名
        if (controllerPackageName == null) {
            controllerPackageName = DEFAULT_EMPTY_PACKAGES;
        }
        return controllerPackageName;
    }

    /**
     * 是否是目标包
     * @param packageName 包名称
     * @return 是否是目标包
     */
    public boolean isExpectedPackage(String packageName) {
        return packageDefaultMetadataMap.keySet().stream()
                .anyMatch(k -> DEFAULT_EMPTY_PACKAGES.equals(k) || packageName.startsWith(k));
    }
}
